﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.NetworkInformation;

namespace LanSpeedTool
{
    public partial class SpeedTool : Form
    {
        Ping netPing = null;
        bool isRunning = false;
        int requestTimes = 4;
        int completedTimes = 0;
        int lostCnt = 0;
        int exactCnt = 0;
        long totalMillSeconds = 0;
        int totalBits = 0;
        int maxShowCnt = 16;
        decimal nSeconds = 0;
        DataTable resultDt = null;
        ManualResetEvent mrEvent = new ManualResetEvent(false); 

        public SpeedTool()
        {
            InitializeComponent();
            tbAddress.Text = "127.0.0.1";
            netPing = new Ping();
            netPing.PingCompleted += P_PingCompleted;
            InitDataGridView();
        }

        private void InitDataGridView()
        {
            resultDt = new DataTable();
            resultDt.Columns.Add("序号");
            resultDt.Columns.Add("地址");
            resultDt.Columns.Add("长度(字节)");
            resultDt.Columns.Add("耗时(毫秒)");
            resultDt.Columns.Add("TTL(次数)");
            resultDt.Columns.Add("状态");
            resultDt.Columns.Add("信息");

            foreach(DataColumn col in resultDt.Columns)
            {
                dgvList.Columns.Add(col.ColumnName, col.ColumnName);
            }

            dgvList.Columns[0].MinimumWidth = 100;
            dgvList.Columns[1].MinimumWidth = 120;
            dgvList.Columns[2].MinimumWidth = 120;
            dgvList.Columns[3].MinimumWidth = 90;
            dgvList.Columns[4].MinimumWidth = 80;
            dgvList.Columns[5].MinimumWidth = 80;
            dgvList.Columns[6].MinimumWidth = 150;
        }

        private void BtnStateChange(int state)
        {

            if (state == 0)
            {
                btnStart.Enabled = true;
                btnStop.Enabled = false;
            }
            else
            {
                btnStop.Enabled = true;
                btnStart.Enabled = false;
            }
        }

        private void PingSend()
        {
            string ip = tbAddress.Text.Trim();
            if (string.IsNullOrEmpty(ip))
            {
                MessageBox.Show("请输入地址");
                return;
            }

            resultDt.Rows.Clear();
            lostCnt = 0;
            exactCnt = 0;
            totalMillSeconds = 0;
            completedTimes = 0;
            int pkgSize = (int)ndPkgSize.Value;
            requestTimes = (int)ndRequestCount.Value;
            int pkgTimeout = (int)ndTimeout.Value;

            byte[] buffer = GeneratePkg(pkgSize);
            isRunning = true;

            Invoke((Action)delegate
            {
                BtnStateChange(1);
                dgvList.Rows.Clear();
            });

            if (requestTimes > 0)
            {
                for (int i = 0; i < requestTimes; ++i)
                {
                    if (isRunning == false) break;
                    bool rt = Request(pkgTimeout, buffer, ip, pkgTimeout);
                    if (rt == false) break;
                }
            }
            else
            {
                while (true)
                {
                    if (isRunning == false) break;
                    bool rt = Request(pkgTimeout, buffer, ip, pkgTimeout);
                    if (rt == false) break;
                    Thread.Sleep(200);
                }
            }
            Invoke((Action)delegate
            {
                BtnStateChange(0);
            });
        }

        private bool Request(int pkgSize, byte[] buffer, string ip, int timeout)
        {
            if (pkgSize == 0)
            {
                netPing.SendAsync(ip, timeout, null);
            }
            else
            {
                netPing.SendAsync(ip, timeout, buffer, null);
            }

            if (isRunning == false) return false;
            mrEvent.Reset();
            mrEvent.WaitOne();
            return true;
        }

        private byte[] GeneratePkg(int size)
        {
            byte[] buffer = new byte[size];
            for (int i = 0; i < size; ++i)
            {
                buffer[i] = 255;
            }
            return buffer;
        }

        private void PingCancel()
        {
            isRunning = false;
            netPing.SendAsyncCancel();
        }

        private void P_PingCompleted(object sender, PingCompletedEventArgs e)
        {
            Invoke((Action)delegate
            {
                if (resultDt.Rows.Count >= maxShowCnt)
                {
                    dgvList.Rows.Clear();
                    resultDt.Rows.Clear();
                }
            });

            bool isMb = false;
            decimal rate = 0m;
            string info = string.Empty;

            if (e.Cancelled == false)
            {
                if (e.Reply.Status == IPStatus.TimedOut)
                {
                    ++lostCnt;
                }
                else
                {
                    ++exactCnt;
                    totalMillSeconds += e.Reply.RoundtripTime;
                    totalBits += e.Reply.Buffer.Length;
                    if (e.Reply.RoundtripTime == 0) nSeconds += 0.04m;
                    if (nSeconds >= 1m)
                    {
                        totalMillSeconds += (long)nSeconds;
                        nSeconds = 0;
                    }
                }

                object[] row = new object[7];
                row[0] = ++completedTimes;
                row[1] = e.Reply.Address.ToString();
                row[2] = e.Reply.Buffer.Length;
                row[3] = e.Reply.RoundtripTime == 0 ? "<1" : e.Reply.RoundtripTime.ToString();
                row[4] = e.Reply.Options == null ? 0 : e.Reply.Options.Ttl;
                row[5] = e.Reply.Status.ToString();
                row[6] = e.Error == null ? string.Empty : e.Error.ToString();
                resultDt.Rows.Add(row);

                rate = (totalBits / (1024m * (totalMillSeconds <= 0 ? 1 : totalMillSeconds))) * 1000m;
                info = "【参考网速约:" + Math.Round(rate / 128, 1, MidpointRounding.AwayFromZero) + "Mb/s】";

                if (rate > 1024)
                {
                    isMb = true;
                    rate = rate / 1024;
                }
                rate = Math.Round(rate, 2, MidpointRounding.AwayFromZero);
            }

            Invoke((Action)delegate
            {
                dgvList.RowCount = resultDt.Rows.Count;
                tsPkgCount.Text = "正确包总数:" + exactCnt;
                tsPkgLostCount.Text = "丢包总数:" + lostCnt;
                tsNetSpeedRate.Text = "速率:" + rate + (isMb ? "mb/s" : "kb/s") + info;

                if ((requestTimes > 0 && completedTimes == requestTimes)
                || e.Cancelled)
                {
                    isRunning = false;
                    BtnStateChange(0);
                }
                Application.DoEvents();
            });
            mrEvent.Set();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
           var rt= Task.Factory.StartNew(() =>
            {
                PingSend();
            });
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            PingCancel();
        }

        private void SpeedTool_Shown(object sender, EventArgs e)
        {
            BtnStateChange(0);
        }

        private void dgvList_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
        {
            if (e.RowIndex >= resultDt.Rows.Count || e.ColumnIndex >= resultDt.Columns.Count) return;

            e.Value = resultDt.Rows[e.RowIndex][e.ColumnIndex];
        }
    }
}
